#include "fcsp.h"

namespace fcsp {
using namespace std;

FCSP::FCSP() { srand(time(NULL)); }
FCSP::~FCSP() { }

BinaryConstraint &FCSP::AddConstraint(Variable &x, Variable &y) {
  BinaryConstraint constraint(&y, &x);
  bool res = (GetConstraint(y, x)).second;
  if (res) {
    RemoveConstraint(y, x);
  }
  binary_constraints_.push_back(constraint);

  constraint = BinaryConstraint(&x, &y);
  res = (GetConstraint(x, y)).second;
  if (res) {
    RemoveConstraint(x, y);
  }
  binary_constraints_.push_back(constraint);
  return (binary_constraints_.back());
}

BinaryConstraint &FCSP::AddConstraint(Variable &x, Variable &y,
    map< int, map<int, double> > preferences) {
  map< int, map<int, double> > preferences_inv;
  for ( map< int, map<int, double> >::iterator it1 = preferences.begin(); it1 != preferences.end();
        ++it1) {
    for (map<int, double>::iterator it2 = (*it1).second.begin(); it2 != (*it1).second.end();
        ++it2) {
      preferences_inv[(*it2).first][(*it1).first] = (*it2).second;
    }
  }
  BinaryConstraint constraint(&y, &x, preferences_inv);
  bool res = (GetConstraint(y, x)).second;
  if (res) {
    RemoveConstraint(y, x);
  }
  binary_constraints_.push_back(constraint);

  constraint = BinaryConstraint(&x, &y, preferences);
  res = (GetConstraint(x, y)).second;
  if (res) {
    RemoveConstraint(x, y);
  }
  binary_constraints_.push_back(constraint);
  return (binary_constraints_.back());
}

pair<BinaryConstraint&, bool> FCSP::GetConstraint(Variable &x, Variable &y) {
  BinaryConstraint constraint(&x, &y);
  list<BinaryConstraint>::iterator it =
    find(binary_constraints_.begin(), binary_constraints_.end(), constraint);
  pair<BinaryConstraint&, bool> res((*it), it != binary_constraints_.end());
  return res;
}

void FCSP::RemoveConstraint(Variable &x, Variable &y) {
  BinaryConstraint constraint(&x, &y);
  list<BinaryConstraint>::iterator it =
    find(binary_constraints_.begin(), binary_constraints_.end(), constraint);
  
  if (it != binary_constraints_.end()) {
    (*it).Destroy();
    binary_constraints_.erase(it);
  }

  constraint = BinaryConstraint(&y, &x);
  it = find(binary_constraints_.begin(), binary_constraints_.end(), constraint);

  if (it != binary_constraints_.end()) {
    (*it).Destroy();
    binary_constraints_.erase(it);
  }
}

double FCSP::GetConstraintPref(Variable &x, Variable &y, int value_x, int value_y) {
  double preference = kLowerPref;
  pair<const BinaryConstraint&, bool> res = GetConstraint(x, y);
  if (res.second) {
    const BinaryConstraint &constraint = res.first;
    preference = constraint.GetPreference(value_x, value_y);
  }
  return preference;
}

void FCSP::SetConstraintPref(Variable &x, Variable &y, int value_x, int value_y, double pref) {
  pair<BinaryConstraint&, bool> res1 = GetConstraint(x, y);
  pair<BinaryConstraint&, bool> res2 = GetConstraint(y, x);
  if (res1.second && res2.second) {
    BinaryConstraint &constraint1 = res1.first;
    BinaryConstraint &constraint2 = res2.first;
    constraint1.SetPreference(value_x, value_y, pref);
    constraint2.SetPreference(value_y, value_x, pref);
  }
}

void FCSP::SetVariablePref(Variable &x, int value_x, double pref) {
  pair<Variable &, bool> res = GetVariable(x);
  if (res.second) {
    Variable &variable = res.first;
    variable.SetPreference(value_x, pref);
  }
}

Variable &FCSP::AddVariable() {
  Variable x;
  variables_.push_back(x);
  return (variables_.back());
}

Variable &FCSP::AddVariable(set<int> &values) {
  map<int, double> preferences;
  for (set<int>::iterator it = values.begin(); it != values.end(); ++it)
    preferences[(*it)] = kLowerPref;
  return AddVariable(preferences);    
}

Variable &FCSP::AddVariable(map<int, double> &preferences) {
  Variable x(preferences);
  pair<const Variable&, bool> res = GetVariable(x);
  if (res.second) {
    RemoveVariable(x);
  }
  variables_.push_back(x);
  return (variables_.back());
}

pair<Variable&, bool> FCSP::GetVariable(Variable &x) {
  list<Variable>::iterator it = find(variables_.begin(), variables_.end(), x);
  pair<Variable&, bool> res(*it, it != variables_.end());
  return res;
}

void FCSP::RemoveVariable(Variable &x) {
  list<Variable>::iterator it = find(variables_.begin(), variables_.end(), x);
  
  if (it != variables_.end()) {
    variables_.erase(it);
  }
}

void FCSP::GenVariablePrefs(int var_card, double tightness, map<int, double> &preferences) {
   preferences.clear();
  
  if (var_card > 0) {
    double preference = kLowerPref;
    
    for (int i = 0; i < var_card; ++i) {
      preference = static_cast<double>(rand()) / RAND_MAX;
      preferences[i] = (static_cast<double>(rand()) / RAND_MAX > tightness)?
        preference : kLowerPref;
    }
  }
}

void FCSP::GenConstraintPrefs(const Variable &x, const Variable &y,
    double tightness, map< int, map<int, double> > &preferences) {
  map<int, double>::const_iterator x_it,
    y_it;
  double preference = kLowerPref;
  preferences.clear();
    
  for (x_it = x.preferences().begin(); x_it != x.preferences().end(); ++x_it) {
    for (y_it = y.preferences().begin(); y_it != y.preferences().end(); ++y_it) {
      preference = static_cast<double>(rand()) / RAND_MAX;
      preferences[(*x_it).first][(*y_it).first] =
          (static_cast<double>(rand()) / RAND_MAX > tightness)? preference : kLowerPref;
    }
  }
}

void FCSP::GenCSP(int var_num, int var_card, double density, double tightness) {
  map<int, double> variable_prefs;

  variables_.clear();
  binary_constraints_.clear();

  for (int i = 0; i < var_num; ++i) {
    GenVariablePrefs(var_card, tightness, variable_prefs);
    Variable x(variable_prefs);
    variables_.push_back(x);
  }
  int constraint_num = density * ((var_num * (var_num - 1)) / 2);
  vector< vector<int> > var_selector;
  vector<int> var_row;
  vector<int> var_row_index;
  
  for (int i = 0; i < var_num - 1; ++i) {
    var_row_index.push_back(i);
    var_row.clear();
    
    for (int j = i + 1; j < var_num; ++j) {
      var_row.push_back(j);
    }    
    random_shuffle(var_row.begin(), var_row.end());
    var_selector.push_back(var_row);
  }
  
  int var_index1,
    var_index2;
  map< int, map<int, double> > constraint_prefs;
  for (int i = 0; i < constraint_num; ++i) {
    random_shuffle(var_row_index.begin(), var_row_index.end());
    var_index1 = var_row_index.back();
    var_index2 = var_selector[var_index1].back();
    
    var_selector[var_index1].pop_back();    
    if (var_selector[var_index1].size() <= 0) {
      var_row_index.pop_back();
    }
    
    list<Variable>::iterator it1 = variables_.begin(),
      it2 = variables_.begin();
    for (int i = 0; i < var_index1 || i < var_index2; ++i) {
      if (i < var_index1) {
        ++it1;
      }
      if (i < var_index2) {
        ++it2;
      }
    }
    Variable &x = (*it1);
    Variable &y = (*it2);

    GenConstraintPrefs(x, y, tightness, constraint_prefs);
    AddConstraint(x, y, constraint_prefs);
  }
}
} // namespace fcsp
